#include "msg.h"


#define MSG_REQ_SYNC        0xAA
#define MSG_REP_SYNC        0x55
#define HEADER_LEN          5

typedef struct {
    uint8_t sync;
    uint8_t id;
    uint16_t pd_len;
    uint8_t checksum;
    uint8_t* payload;
} __attribute__((packed)) msg_t;

typedef enum {
    MSG_SYNCING,
    MSG_HEADER_ID,
    MSG_HEADER_PDLEN,
    MSG_CHECKSUM,
    MSG_SYNC_DONE
} msg_state_t;

static uint8_t calc_checksum(uint8_t* msg, uint8_t len)
{
    int i = 0;
    uint8_t checksum = 0; 
    for (i = 0; i < len; i++)
    {
        checksum ^= msg[i];
    }
    return checksum;
}

int msg_receive_request(rt_device_t dev, uint8_t* id, uint8_t* payload, uint16_t max_len)
{
    msg_t req;
    uint8_t temp = 0;
    int ret = 0;
    int i = 0;
    msg_state_t state = MSG_SYNCING;
    uint8_t* pdata = RT_NULL;
    uint8_t checksum = 0;
    uint32_t start_time = rt_tick_get();
    req.payload = payload;
    while(1)
    {
        switch (state)
        {
        case MSG_SYNCING:
            ret = rt_device_read(dev, 0, &temp, 1);
            if (ret <= 0)
            {
                if (rt_tick_get() - start_time > 100)
                    return MSG_TRANSFER_ERROR;
                break;
            }
            if (temp != MSG_REQ_SYNC)
                break;
            start_time = rt_tick_get();
            req.sync = temp;
            state = MSG_HEADER_ID;
            break;
        case MSG_HEADER_ID:
            ret = rt_device_read(dev, 0, &req.id, 1);
            if (ret <= 0)
            {
                if (rt_tick_get() - start_time > 20)
                    return MSG_TRANSFER_ERROR;
                break;
            }

            state = MSG_HEADER_PDLEN;
            *id = req.id;
            start_time = rt_tick_get();
            break;
        case MSG_HEADER_PDLEN:
            pdata = (uint8_t*)&req.pd_len;
            i = 0;
            while(i != 2)
            {
                ret = rt_device_read(dev, 0, pdata, 1);
                if (ret <= 0)
                {
                    if (rt_tick_get() - start_time > 20)
                        return MSG_TRANSFER_ERROR;
                    break;
                }
                pdata ++;
                i++;
            }
            start_time = rt_tick_get();
            state = MSG_CHECKSUM;
            break;
        case MSG_CHECKSUM:
            ret = rt_device_read(dev, 0, &req.checksum, 1);
            if (ret <= 0)
            {
                if (rt_tick_get() - start_time > 20)
                    return MSG_TRANSFER_ERROR;
                break;
            }
                
            checksum = calc_checksum((uint8_t*)&req, HEADER_LEN - 1);
            if (checksum != req.checksum)
                return MSG_TRANSFER_ERROR;
            state = MSG_SYNC_DONE;
            start_time = rt_tick_get();
            break;
        case MSG_SYNC_DONE:
            {
                uint16_t remain = req.pd_len;
                pdata = req.payload;
                while (remain)
                {
                    ret = rt_device_read(dev, 0, pdata, remain);
                    if (ret <= 0)
                    {
                        if (rt_tick_get() - start_time > 100)
                            return MSG_TRANSFER_ERROR;
                        continue;
                    }
                        
                    remain -= ret;
                    pdata += ret;
                }
                return MSG_TRANSFER_DONE;
            }

        }
        rt_thread_mdelay(1);
    }
    return MSG_TRANSFER_DONE;
}

int msg_send_response(rt_device_t dev, uint8_t id, uint8_t* payload, uint16_t pd_len)
{
    msg_t rep;
    rep.sync = MSG_REP_SYNC;
    rep.pd_len = pd_len;
    rep.id = id;
    rep.checksum = calc_checksum((uint8_t*)&rep, HEADER_LEN - 1);
    int ret = 0;
    int len = 0;
    uint8_t* pdata = (uint8_t*)&rep;
    int wr_len = HEADER_LEN;
    while (len != wr_len)
    {
        ret = rt_device_write(dev, 0, pdata, wr_len - len);
        if (ret <= 0)
            continue;
        pdata += ret;
        len += ret;
    }
    len = 0;
    pdata = payload;
    wr_len = pd_len;
    while (len != wr_len)
    {
        ret = rt_device_write(dev, 0, pdata, wr_len - len);
        if (ret <= 0)
            continue;
        pdata += ret;
        len += ret;
    }
    return MSG_TRANSFER_DONE;
}
